home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume1 / bourne / part1 next >
Encoding:
Internet Message Format  |  1986-11-30  |  55.4 KB

  1. Date: Fri, 7 Jun 85 13:55:07 edt
  2. From: Arnold Robbins <gatech!arnold>
  3. Subject: Bourne shell history + tilde + job control + more (Part 1 of 9)
  4. Newsgroups: mod.sources
  5.  
  6. This and the following eight postings consist of files and context diffs
  7. to add many long-desired features into the Bourne shell. The details of
  8. the new features are listed below in README.gt.sh.  This is a list of
  9. what each article contains
  10.  
  11. Part 1    -- README.gt.sh, new .c files needed for the shell, miscellany
  12.  
  13. Part 2    -- Context diffs of C code for 4.2 BSD /bin/sh
  14. Part 3    -- Context diffs of C code for 4.2 BSD /bin/sh
  15. Part 4    -- Context diffs of sh.1 for 4.2 BSD /bin/sh
  16.  
  17. Part 5    -- Context diffs of C code for System V Release 2 /bin/sh
  18. Part 6    -- Context diffs of C code for System V Release 2 /bin/sh
  19. Part 7    -- Context diffs of sh.1 for System V Release 2 /bin/sh
  20.  
  21. Part 8    -- Context diffs of C code for BRL Unix /usr/5bin/sh
  22. Part 9    -- Context diffs of sh.1 for BRL Unix /usr/5bin/sh
  23.  
  24. I am sorry that there are so many articles; I had to do it this way to
  25. insure that each one would be less that 64K in size.
  26.  
  27. Arnold Robbins
  28. arnold@gatech.{CSNET, UUCP}
  29. ------------------ tear along perforations ------------
  30. #!/bin/sh
  31. # This is a shell archive, meaning:
  32. # 1. Remove everything above the #!/bin/sh line.
  33. # 2. Save the resulting text in a file.
  34. # 3. Execute the file with /bin/sh (not csh) to create the files:
  35. #    README.gt.sh
  36. #    Bugs
  37. #    signal.c
  38. #    ulimit.c
  39. #    jobs.c
  40. #    homedir.c
  41. #    history.c
  42. #    sample.shrc
  43. #    aliases.sh
  44. # This archive created: Fri Jun  7 13:49:23 1985
  45. # By:    Arnold Robbins (Pr1mebusters!)
  46. export PATH; PATH=/bin:$PATH
  47. echo shar: extracting "'README.gt.sh'" '(7022 characters)'
  48. if test -f 'README.gt.sh'
  49. then
  50.     echo shar: over-writing existing file "'README.gt.sh'"
  51. fi
  52. cat << \SHAR_EOF > 'README.gt.sh'
  53. README.gt.sh --- README file for Georgia Tech mods to the shell
  54.  
  55. This and the following eight postings should be all you need to add the
  56. following features to the Bourne shell:
  57.  
  58. 1.  BSD style job control on Berkeley Systems. Code to outwit symbolic
  59.     links for shells which have 'pwd' built in.  Also code to print a
  60.     resource usage summary after every command on BSD systems.  These are
  61.     courtesy of the folks at BRL, who gave me permission to post their
  62.     code.
  63.  
  64. 2.  The ability to catch Control-D's, and force you to use "exit", also
  65.     courtesy of BRL.  This will work whether or not you are on a BSD system.
  66.  
  67. 3.  The <> I/O redirecter is now documented, and also works (courtesy of
  68.     ihnp4!trwrb!lcc!brian's recent posting in net.unix-wizards).
  69.  
  70. 4.  The shell parameter $+ gives you the parent process id of the shell.
  71.     It will track the value of the getppid() system call.
  72.  
  73. 5.  The shell will read in $HOME/.shrc on startup, if that file exists.
  74.     This file is read *after* /etc/profile and $HOME/.profile (unlike the
  75.     csh, which readc ~/.cshrc before ~/.login).
  76.  
  77. 6.  The ~ and ~person notation is understood, both for command line arguments,
  78.     and in the PATH and CDPATH shell variables.
  79.  
  80. 7.  Special sequences in PS1 (the shell's prompt string) will print useful
  81.     info at the prompt.  Currently, you can get or all or some or none of:
  82.     the time of day
  83.     your current directory (if the shell has pwd is built in)
  84.     your machine's hostname
  85.     your login name
  86.     the current 'event' number for the ...
  87.  
  88. 8.  History mechanism.  The history mechanism is powerful, yet easy to use.
  89.     Although different from the csh's, it is somewhat more general and
  90.     orthogonal.  The shell will save history across login sessions,
  91.     automatically restoring on login, and saving on exit or on the exec builtin.
  92.  
  93. On a Pyramid, the shell has some additional capabilities:
  94.  
  95. 9.  The 'att', 'ucb' and 'universe' commands are built in.
  96.  
  97. 10. The $UNIVERSE shell variable tracks the current universe.
  98.  
  99. 11. An additional sequence for the prompt to print the current universe.
  100.  
  101. *****************
  102.  
  103. I am posting diffs for the versions of the Bourne shell listed below.
  104. Here are the instructions for setting up the makefile for each shell,
  105. depending on your target machine and OS.  For all versions, you will need
  106. the files history.c and homedir.c; these have been added to the makefile,
  107. but will only be posted once.
  108.  
  109. It will help if you have the 'patch' program.  It was just reposted around
  110. the middle of May, 1985 (Version 1.3). If you don't have it, someone at your
  111. site or someone you know probably does.
  112.  
  113. First, unshar this article.  You wil have the following files:
  114.  
  115. README.gt.sh        # this file
  116. Bugs            # some known bugs in what I've added
  117. signal.c        # courtesy of BRL
  118. ulimit.c        # courtesy of BRL
  119. jobs.c            # courtesy of BRL
  120. homedir.c
  121. history.c
  122. sample.shrc
  123. aliases.sh
  124.  
  125. Next, make a new directory, and copy the source for the version of the shell
  126. that you are going to modify, into it.
  127. Copy the *unformatted* man page for that shell into the directory also.
  128. Move history.c and homedir.c into the directory.
  129.  
  130. Find which version of the shell you have, and follow the supplementary
  131. instructions below.
  132.  
  133. 1. The Berkeley /bin/sh as distributed with 4.2, for 4.2BSD.
  134.     Add the files signal.c and jobs.c to the directory.
  135.     Get parts 2, 3 and 4, and run patch on them.
  136.     Run make.
  137.  
  138.     On a Pyramid, follow the previous paragraph but don't run make yet.
  139.     First, make sure you are in the 'ucb' universe.
  140.     Remove all references to signal.c and signal.o from the makefile.
  141.     Now run make.
  142.  
  143. 2. The System V Release 2 shell as distributed from ATT (for the vax).
  144.     On System V systems, the job stuff is conditionally compiled in.
  145.     Get parts 5, 6, and 7, and run patch on them.
  146.     Remove the -DJOBS and all references to signal.c, ulimit.c,
  147.     jobs.c, and their .o files, from the makefile (sh.mk).
  148.     Run make (make -f sh.mk).
  149.     The sh.1 file will need editing to remove any reference to the 'J'
  150.     flag, the 'I' flag, and to all the job control features.
  151.  
  152.     On BSD systems, copy signal.c, ulimit.c, and jobs.c into the directory.
  153.     Get parts 5, 6, and 7, and run patch on them.
  154.     Run make. (make -f sh.mk)
  155.  
  156.     On a Pyramid, copy ulimit.c, and jobs.c into the directory.
  157.     Run patch.  Remove signal.o and signal.c from the makefile,
  158.     and the testing for u370 for xec.c. Be sure to be in the ucb
  159.     universe, and then run make. (make -f sh.mk)
  160.  
  161. 3. The System V Release 2 shell as modified at BRL, for BRL Unix.
  162.     Get parts 8 and 9, and run patch on them.
  163.  
  164.     There are two ways to compile this version under BRL Unix.
  165.  
  166.     A. Use the standard BSD make and cc. In this case, all you will need
  167.        is history.c and homedir.c.  Run make.
  168.  
  169.     B. Use the System V emulation, /usr/5bin/make and /usr/5bin/cc.
  170.        In this case, make sure your PATH is set properly. Remove the
  171.        -DBSD from the makefile, and also any references to signal.c,
  172.        and ulimit.c, and their .o files.  Now run /usr/5bin/make.
  173.     
  174.     On a Pyramid, run patch.  Edit the makefile to exclude signal.c
  175.     and signal.o.  Be sure to be in the ucb universe, and then run make.
  176.  
  177. **********
  178.  
  179. Aliases.sh is a bunch of useful shell functions --- only of value for one
  180. or the other of the System V Release 2 Versions of the shell.
  181.  
  182. Sample.shrc is a sample .shrc file.  In particular, it gives you some
  183. compatibility with the Korn shell.  It sets PPID=$+, and if the ENV
  184. environment variable is set to a file name, it will source that file
  185. (that is how the ksh does ~/.shrc).
  186.  
  187. If you want job control to be turned on automatically (on a BSD system),
  188. add the line
  189.     set -J
  190. to /etc/profile (or /usr/5lib/profile, depending).  This will turn job
  191. control on for login shells.
  192.  
  193. ***************************************
  194.  
  195. I have tested the shells on the following systems:
  196.  
  197.         Vax (BRL Unix) |  Pyramid  | 3B2 (S5R2)  | 3B20A (S5R2)
  198. BSD shell        x         x
  199. S5R2 shell        x         x        x        x
  200. BRL/S5R2 shell        x         x        x
  201.  
  202. As should be clear from the above table, I have only had access to four
  203. different kinds of machines.  If you are running on some other kind of
  204. hardware, and/or another flavor of Unix (V7, Xenix, Perkin Elmer, whatever),
  205. and you successfully add these mods to the shell, please let me know.  Also
  206. send me any diff listings you may have had to generate.  I am particularly
  207. interested to know if it will still fit on a PDP-11.
  208.  
  209. ***************************************
  210.  
  211. Please don't send me any flames to the effect "You should use the C-shell".
  212. Here is a case where I can have my cake and eat it too, and this should be
  213. a big win for people who only have System V and don't have the ksh.
  214.  
  215. I hope that these modifications to the shell help to meet a need out there
  216. in the real world.  If you find any bugs, please let me know.
  217.  
  218. Arnold Robbins
  219. CSNET:    arnold@gatech    ARPA:    arnold%gatech.csnet@csnet-relay.arpa
  220. UUCP:    { akgua, allegra, hplabs, ihnp4, seismo, ut-sally }!gatech!arnold
  221.  
  222. School of Information and Computer Science
  223. Georgia Institute of Technology
  224. 225 North Avenue, N.W.
  225. Atlanta, Georgia  30332
  226. (404) 894-3658
  227. SHAR_EOF
  228. echo shar: extracting "'Bugs'" '(2013 characters)'
  229. if test -f 'Bugs'
  230. then
  231.     echo shar: over-writing existing file "'Bugs'"
  232. fi
  233. cat << \SHAR_EOF > 'Bugs'
  234. Bugs -- known problems in the shell.
  235.  
  236. The "suspend" built-in command is *very* naive.  E.g. cranking up a
  237. subshell from vi, and then suspending it, will leave you sort of in limbo.
  238. A control-Z will then suspend the vi.  Then a 'fg' command foregrounds the
  239. stopped sub-shell. Control-D'ing the subshell puts you back in vi.
  240.  
  241. If the shell is being run from a terminal, and you interrupt it in the
  242. middle of doing a here document (cat << FOO ..., interrupt before the FOO),
  243. then if history was turned on, you will be left with history turned off.
  244. Use set +H to turn it back on.
  245.  
  246. I do not have access to a PDP-11, so there will probably be problems
  247. trying to move this stuff to a small address space machine.  Let me know
  248. what you encounter, and I will encorporate any diffs that people send back
  249. to me.
  250.  
  251. A recent posting in net.micro.att indicated that the Unix PC's window
  252. manager uses $HOME/.history to save things. This is also the default for
  253. where this history mechanism keeps things -- change the value of HISTFILE in
  254. your .profile to be something different, and export it, if you're on a Unix PC.
  255.  
  256. ****************
  257.  
  258. There are probably bugs in the code I have added to the shell.
  259. I think I have caught everything, but I can't guarantee.  If you discover
  260. any problems, please let me know, so that I can track them down and fix
  261. them.
  262.  
  263. I regard this as a "first iteration."  In other words, I will not be suprised
  264. if there are bugs.  I am counting on the net to be friendly enough to let
  265. me know about any that may be discovered.  I am also open to suggestions for
  266. other fixes or additions to the shell.  As things come in to me, I will
  267. incorporate what I can, and hopefully post a new set of revisions.
  268.  
  269. Meanwhile, enjoy!
  270.  
  271. Arnold Robbins
  272. CSNET:    arnold@gatech    ARPA:    arnold%gatech.csnet@csnet-relay.arpa
  273. UUCP:    { akgua, allegra, hplabs, ihnp4, seismo, ut-sally }!gatech!arnold
  274.  
  275. School of Information and Computer Science
  276. Georgia Institute of Technology
  277. 225 North Avenue, N.W.
  278. Atlanta, Georgia  30332
  279. (404) 894-3658
  280. SHAR_EOF
  281. echo shar: extracting "'signal.c'" '(3896 characters)'
  282. if test -f 'signal.c'
  283. then
  284.     echo shar: over-writing existing file "'signal.c'"
  285. fi
  286. cat << \SHAR_EOF > 'signal.c'
  287. /*
  288.     signal -- old system call emulation for 4.2BSD (VAX version)
  289.         (adapted from BRL UNIX System V emulation for 4.2BSD)
  290.  
  291.     last edit:    25-Aug-1984    D A Gwyn
  292.  
  293.     NOTE:  Although this module is VAX-specific, it should be
  294.     possible to adapt it to other fairly clean implementations of
  295.     4.2BSD.  The difficulty lies in avoiding the automatic restart
  296.     of certain system calls when the signal handler returns.  I use
  297.     here a trick first described by Donn Seeley of UCSD Chem. Dept.
  298. */
  299.  
  300. #include    <errno.h>
  301. #include    <signal.h>
  302. #include    <syscall.h>
  303.  
  304. extern int    sigvec();
  305. extern int    sigsetmask();
  306.  
  307. extern        etext;
  308. extern int    errno;
  309.  
  310. static int    (*handler[NSIG])() =    /* "current handler" memory */
  311.     {
  312.     BADSIG                /* initially, unknown state */
  313.     };
  314. static int    inited = 0;        /* for initializing above */
  315.  
  316. static int    catchsig();
  317. static int    ret_eintr();
  318.  
  319. int    (*
  320. signal( sig, func )            /* returns previous handler */
  321.     )()
  322.     register int    sig;        /* signal affected */
  323.     register int    (*func)();    /* new handler */
  324.     {
  325.     register int    (*retval)();    /* previous handler value */
  326.     struct sigvec    oldsv;        /* previous state */
  327.     struct sigvec    newsv;        /* state being set */
  328.  
  329.     if ( func >= (int (*)())&etext )    /* "lint" hates this */
  330.         {
  331.         errno = EFAULT;
  332.         return BADSIG;        /* error */
  333.         }
  334.  
  335.     /* cancel pending signals */
  336.     newsv.sv_handler = SIG_IGN;
  337.     newsv.sv_mask = newsv.sv_onstack = 0;
  338.     if ( sigvec( sig, &newsv, &oldsv ) != 0 )
  339.         return BADSIG;        /* error */
  340.  
  341.     /* C language provides no good way to initialize handler[] */
  342.     if ( !inited )            /* once only */
  343.         {
  344.         register int    i;
  345.  
  346.         for ( i = 1; i < NSIG; ++i )
  347.             handler[i] = BADSIG;    /* initialize */
  348.  
  349.         ++inited;
  350.         }
  351.  
  352.     /* the first time for this sig, get state from the system */
  353.     if ( (retval = handler[sig-1]) == BADSIG )
  354.         retval = oldsv.sv_handler;
  355.  
  356.     handler[sig-1] = func;    /* keep track of state */
  357.  
  358.     if ( func == SIG_DFL )
  359.         newsv.sv_handler = SIG_DFL;
  360.     else if ( func != SIG_IGN )
  361.         newsv.sv_handler = catchsig;    /* actual sig catcher */
  362.  
  363.     if ( func != SIG_IGN        /* sig already being ignored */
  364.       && sigvec( sig, &newsv, (struct sigvec *)0 ) != 0
  365.        )
  366.         return BADSIG;        /* error */
  367.  
  368.     return retval;            /* previous handler */
  369.     }
  370.  
  371.  
  372. /* # bytes to skip at the beginning of C ret_eintr() function code: */
  373. #define    OFFSET    2            /* for VAX .word reg_mask */
  374.  
  375. /* PC will be pointing at a syscall if it is to be restarted: */
  376. typedef unsigned char    opcode;        /* one byte long */
  377. #define    SYSCALL        ((opcode)0xBC)    /* VAX CHMK instruction */
  378. #define    IMMEDIATE    ((opcode)0x8F)    /* VAX immediate addressing */
  379.  
  380.  
  381. /*ARGSUSED*/
  382. static int
  383. catchsig( sig, code, scp )        /* signal interceptor */
  384.     register int        sig;    /* signal number */
  385.     int            code;    /* code for SIGILL, SIGFPE */
  386.     register struct sigcontext    *scp;    /* -> interrupted context */
  387.     {
  388.     register int        (*uhandler)();    /* user handler */
  389.     register opcode        *pc;    /* for snooping instructions */
  390.     struct sigvec        newsv;    /* state being set */
  391.  
  392.     /* at this point, sig is blocked */
  393.  
  394.     uhandler = handler[sig - 1];
  395.  
  396.     /* most UNIXes usually want the state reset to SIG_DFL */
  397.     if ( sig != SIGILL && sig != SIGTRAP )
  398.         {
  399.         handler[sig-1] = newsv.sv_handler = SIG_DFL;
  400.         newsv.sv_mask = newsv.sv_onstack = 0;
  401.         (void)sigvec( sig, &newsv, (struct sigvec *)0 );
  402.         }
  403.  
  404.     (void)sigsetmask( scp->sc_mask );    /* restore old mask */
  405.  
  406.     /* at this point, sig is not blocked, usually have SIG_DFL;
  407.        a longjmp may safely be taken by the user signal handler */
  408.  
  409.     (void)(*uhandler)( sig );    /* user signal handler */
  410.  
  411.     /* must now avoid restarting certain system calls */
  412.     pc = (opcode *)scp->sc_pc;
  413.     if ( *pc++ == SYSCALL
  414.       && (*pc == SYS_read || *pc == SYS_write || *pc == SYS_ioctl
  415.        || *pc++ == IMMEDIATE
  416.        && (*pc == SYS_wait || *pc == SYS_readv || *pc == SYS_writev)
  417.          )
  418.        )
  419.         scp->sc_pc = (int)ret_eintr + OFFSET;
  420.  
  421.     /* return here restores interrupted context */
  422.     }
  423.  
  424.  
  425. static int
  426. ret_eintr()                /* substitute for system call */
  427. {
  428.     errno = EINTR;
  429.     return -1;
  430. }
  431. SHAR_EOF
  432. echo shar: extracting "'ulimit.c'" '(855 characters)'
  433. if test -f 'ulimit.c'
  434. then
  435.     echo shar: over-writing existing file "'ulimit.c'"
  436. fi
  437. cat << \SHAR_EOF > 'ulimit.c'
  438. /*
  439.     ulimit -- system call emulation for Bourne shell on 4.2BSD
  440.  
  441.     last edit:    22-Aug-1983    D A Gwyn
  442. */
  443.  
  444. #include    <errno.h>
  445.  
  446. extern int    getrlimit(), setrlimit();
  447. extern int    errno;
  448.  
  449. long
  450. ulimit( cmd, newlimit )
  451.     int    cmd;            /* subcommand */
  452.     long    newlimit;        /* desired new limit */
  453.     {
  454.     struct    {
  455.         long    rlim_cur;
  456.         long    rlim_max;
  457.         }    limit;        /* data being gotten/set */
  458.  
  459.     switch ( cmd )
  460.         {
  461.     case 1:             /* get file size limit */
  462.         if ( getrlimit( 1, &limit ) != 0 )
  463.             return -1L;    /* errno is already set */
  464.         return limit.rlim_max / 512L;
  465.  
  466.     case 2:             /* set file size limit */
  467.         limit.rlim_cur = limit.rlim_max = newlimit * 512L;
  468.         return setrlimit( 1, &limit );
  469.  
  470.     case 3:             /* get maximum break value */
  471.         if ( getrlimit( 2, &limit ) != 0 )
  472.             return -1L;    /* errno is already set */
  473.         return limit.rlim_max;
  474.  
  475.     default:
  476.         errno = EINVAL;
  477.         return -1L;
  478.         }
  479.     }
  480. SHAR_EOF
  481. echo shar: extracting "'jobs.c'" '(11717 characters)'
  482. if test -f 'jobs.c'
  483. then
  484.     echo shar: over-writing existing file "'jobs.c'"
  485. fi
  486. cat << \SHAR_EOF > 'jobs.c'
  487. /*
  488.  *  JOBS.C -- job control for Bourne shell
  489.  *
  490.  *  created by Ron Natalie, BRL
  491.  *  slight changes by Doug Gwyn
  492.  *  some more slight changes by Arnold Robbins (mainly for the BSD /bin/sh)
  493.  */
  494.  
  495. #include "defs.h"
  496. #include "sym.h"
  497.  
  498. #ifndef TAB    /* very original, early /bin/sh */
  499. #include <signal.h>
  500. #define comptr(x)    ((COMPTR) x)
  501. #define lstptr(x)    ((LSTPTR) x)
  502. #define forkptr(x)    ((FORKPTR) x)
  503. #define parptr(x)    ((PARPTR) x)
  504. #define forptr(x)    ((FORPTR) x)
  505. #define whptr(x)    ((WHPTR) x)
  506. #define ifptr(x)    ((IFPTR) x)
  507. #define swptr(x)    ((SWPTR) x)
  508. #endif
  509.  
  510. #if BSD || (JOBS && ! BRL)    /* native /bin/sh */
  511. #include <sys/ioctl.h>
  512. #else    /* /usr/5bin/sh */
  513. #include    <sys/_ioctl.h>
  514. #define    ioctl    _ioctl
  515. #define    killpg    _killpg
  516. #define    setpgrp    _setpgrp
  517. #define    TIOCSETD    _IOW( 't', 1, int )
  518. #define    TIOCSPGRP    _IOW( 't', 118, int )
  519. #define    NTTYDISC    2
  520. #endif
  521.  
  522. #define    NJCH    30
  523. #define    JCOMSIZE 50
  524. static struct    j_child  {
  525.     int    j_pgid;
  526.     int    j_status;
  527.     int    j_info;
  528.     char    j_com[JCOMSIZE];
  529.     int    j_jobnum;
  530. } j_children[NJCH];
  531.  
  532. static int    j_number = 1;
  533. static int    j_current = 0;
  534.  
  535. #define    JEMPTY    0
  536. #define    JALIVE    1
  537. #define    JSTOP    2
  538. #define    JBG    3
  539.  
  540. BOOL        j_top_level = TRUE;
  541. int        j_default_pg = 0;
  542. int        j_original_pg = 0;
  543. static int    j_last_pgrp = 0;
  544. static int    j_do(), j_getnumber(), j_stuff();
  545. static void    j_backoff(), j_print_ent(), j_really_reset_pg(), j_setcommand();
  546.     
  547. j_child_post(p, bg, pin, t)
  548.     int        p;
  549.     int        bg;
  550.     register int    pin;
  551.     struct trenod    ***t;
  552. {
  553.     register struct j_child *j = j_children;
  554.  
  555.     if((flags & jobflg) == 0)
  556.         return;
  557.     if(!j_top_level)
  558.         return;
  559.  
  560.     if(!pin)  {
  561.         setpgrp(p, p);
  562.         j_last_pgrp = p;
  563.         if(!bg)  {
  564.             ioctl(1, TIOCSPGRP, &p);
  565.             setpgrp(0, p);
  566.         }
  567.     }
  568.     else
  569.         setpgrp(p, j_last_pgrp);
  570.  
  571.     for(j=j_children; j < &j_children[NJCH]; j++)  {
  572.         if(pin && j->j_pgid == j_last_pgrp)  {
  573.             j_setcommand(j, t);
  574.             return;
  575.         }
  576.  
  577.         if(!pin &&j->j_status == JEMPTY)  {
  578.             j->j_com[0] = 0;
  579.             j->j_status = bg ? JBG : JALIVE;
  580.             j->j_pgid = p;
  581.             j_setcommand(j, t);
  582.             if(bg)  {
  583.                 post(p);
  584.                 j->j_jobnum = j_getnumber();
  585.                 j_print_ent(j);
  586.             }
  587.             else
  588.                 j->j_jobnum = 0;
  589.             return;
  590.         }
  591.     }
  592.     prn(p);
  593.     prs(cjpostr);            /* DAG -- made strings sharable */
  594. }
  595.  
  596. j_child_clear(p)
  597.     register int    p;
  598. {
  599.     register struct j_child *j = j_children;
  600.  
  601.     if(p == 0 || p == -1)
  602.         return;
  603.  
  604.     for(; j < &j_children[NJCH]; j++)
  605.         if(j->j_status == JALIVE && j->j_pgid == p)  {
  606.             j->j_status = JEMPTY;
  607.             if(j->j_jobnum && j->j_jobnum == j_current)
  608.                 j_backoff();
  609.             break;
  610.         }
  611. }
  612.  
  613. j_child_stop(p, sig)
  614.     register int    p;
  615.     int        sig;
  616. {
  617.     register struct j_child *j = j_children; 
  618.  
  619.     for(; j < &j_children[NJCH]; j++)
  620.         if((j->j_status == JALIVE || j->j_status == JBG) && j->j_pgid == p)  {
  621.             j->j_status = JSTOP;
  622.             j->j_info = sig;
  623.             if(j->j_jobnum == 0)
  624.                 j->j_jobnum = j_getnumber();
  625.             j_current = j->j_jobnum;
  626.             prc(NL);
  627.             j_print_ent(j);
  628.             fault(SIGSTOP);
  629.             break;
  630.         }
  631. }
  632.  
  633. j_child_die(p)
  634.     register int    p;
  635. {
  636.     register struct j_child *j = j_children; 
  637.  
  638.     if(p == 0 || p == -1)
  639.         return;
  640.  
  641.     for(; j < &j_children[NJCH]; j++)
  642.         if( j->j_status != JEMPTY && j->j_pgid == p)  {
  643.             j->j_status = JEMPTY;
  644.             if(j->j_jobnum && j->j_jobnum == j_current)
  645.                 j_backoff();
  646.             break;
  647.         }
  648. }
  649.  
  650. j_print()
  651. {
  652.     register struct j_child *j = j_children; 
  653.  
  654.     if((flags & jobflg) == 0)  {
  655.         prs(jcoffstr);        /* DAG */
  656.         return;
  657.     }
  658.  
  659.     await(-2, 1);
  660.     for(; j < &j_children[NJCH]; j++)
  661.         j_print_ent(j);
  662. }
  663.  
  664. static void
  665. j_print_ent(j)
  666.     register struct j_child *j;
  667. {
  668.     if(j->j_status == JEMPTY)
  669.         return;
  670.  
  671.     if(j->j_jobnum == 0) {
  672.         prs(jpanstr);        /* DAG */
  673.         prn(j->j_pgid);
  674.         prc(NL);
  675.     }
  676.     prc('[');
  677.     prn(j->j_jobnum);
  678.     prs(rsqbrk);            /* DAG */
  679.     if(j_current == j->j_jobnum)
  680.         prs(execpmsg);        /* DAG */
  681.     else
  682.         prs(spspstr);        /* DAG */
  683.     prn(j->j_pgid);
  684.     prc(' ');
  685.  
  686.     switch(j->j_status)  {
  687.     case JALIVE:
  688.         prs(fgdstr);        /* DAG */
  689.         break;
  690.     case JSTOP:
  691.         prs(stpdstr);        /* DAG */
  692.         switch(j->j_info)  {
  693.         case SIGTSTP:
  694.             prs(lotspstr);    /* DAG */
  695.             break;
  696.         case SIGSTOP:
  697.             prs(psgpstr);    /* DAG */
  698.             break;
  699.         case SIGTTIN:
  700.             prs(ptinstr);    /* DAG */
  701.             break;
  702.         case SIGTTOU:
  703.             prs(ptoustr);    /* DAG */
  704.             break;
  705.         }
  706.         break;
  707.     case JBG:
  708.         prs(bgdstr);        /* DAG */
  709.         break;
  710.     }
  711.     prc(' ');
  712.     prs(j->j_com);
  713.     prc(NL);
  714. }
  715.  
  716. j_resume(cp, bg)
  717.     char    *cp;
  718.     BOOL    bg;
  719. {
  720.     register struct j_child *j = j_children; 
  721.     int    p;
  722.     
  723.     if((flags & jobflg) == 0)  {
  724.         prs(jcoffstr);        /* DAG */
  725.         return;
  726.     }
  727.  
  728.     if(cp)  {
  729.         p = atoi(cp);
  730.         if(p == 0)  {
  731.             prs(jinvstr);    /* DAG */
  732.             return;
  733.         }
  734.     }
  735.     else
  736.         p = 0;
  737.         
  738.     await(-2, 1);
  739.     if(p == 0 && j_current == 0)  {
  740.         prs(ncjstr);        /* DAG */
  741.         return;
  742.     }
  743.  
  744.     for(; j < &j_children[NJCH]; j++)
  745.         if(j->j_status != JEMPTY)
  746.             if(
  747.                (p != 0 && j->j_pgid == p) ||
  748.                (p == 0 && j->j_jobnum == j_current)
  749.             )  {
  750.                 p = j->j_pgid;
  751.                 if(!bg)  {
  752.                     ioctl(1, TIOCSPGRP, &p);
  753.                     setpgrp(0, p);
  754.                 }
  755.                 j->j_status = bg ? JBG : JALIVE;
  756.                 j_print_ent(j);
  757.                 if(killpg(p, SIGCONT) == -1)  {
  758.                     j->j_status = JEMPTY;
  759.                     break;
  760.                 }
  761.                 if(bg)
  762.                     post(p);
  763.                 else
  764.                     await(p, 0);
  765.                 j_reset_pg();
  766.                 return;
  767.             }
  768.     prn(p);
  769.     prs(nstpstr);            /* DAG */
  770. }
  771.  
  772. char    *
  773. j_macro()
  774. {
  775.     static char    digbuf[40];
  776.     register char    *c;
  777.     register int    i;
  778.     register struct j_child *j = j_children; 
  779.  
  780.     c = digbuf;
  781.     *c++ = '%';
  782.  
  783.     for(;;) {
  784.         *c = readc();
  785.         if( c==(digbuf+1) && *c == '%')  {
  786.             i = j_current;
  787.             break;
  788.         }
  789.         if(!digchar(*c))  {
  790.             peekc = *c | MARK;
  791.             *c = 0;
  792.             i = stoi(digbuf+1);
  793.             break;
  794.         }
  795.         c++;
  796.     }
  797.  
  798.     if(i != 0)
  799.         for(; j < &j_children[NJCH]; j++)
  800.             if(j->j_status != JEMPTY && j->j_jobnum == i)  {
  801.                 itos(j->j_pgid);
  802.                 movstr(numbuf, digbuf);    /* DAG */
  803.                 break;
  804.             }
  805.  
  806.     return digbuf;
  807. }
  808.  
  809. j_reset_pg()
  810. {
  811.     if((flags & jobflg) == 0)
  812.         return;
  813.     if(j_top_level)  {
  814.         ioctl(0, TIOCSPGRP, &j_default_pg);
  815.         setpgrp(0, j_default_pg);
  816.     }
  817. }
  818.  
  819. static void
  820. j_really_reset_pg()
  821. {
  822.     ioctl(0, TIOCSPGRP, &j_original_pg);
  823.     setpgrp(0, j_original_pg);
  824. }
  825.  
  826.  
  827. #include "ctype.h"
  828. extern BOOL trapflg[];
  829.  
  830. j_init()
  831. {
  832.     static int    ldisc = NTTYDISC;    /* BSD ioctl brain damage */
  833.  
  834.     if(flags & jobflg)
  835.         return;
  836.     j_reset_pg();
  837.     trapflg[SIGTTIN] = SIGMOD | 1;
  838.     trapflg[SIGTTOU] = SIGMOD | 1;
  839.     trapflg[SIGTSTP] = SIGMOD | 1;
  840.     trapflg[SIGSTOP] = SIGMOD | 1;
  841.     ignsig(SIGTSTP);
  842.     ignsig(SIGSTOP);    /*  Just to make sure  */
  843.     (void)ioctl( 0, TIOCSETD, &ldisc );    /* DAG -- require "new tty" handler */
  844. /*    flags |= jobflg;    */
  845. }
  846.  
  847. BOOL
  848. j_finish(force)
  849.     BOOL    force;
  850. {
  851.     register struct j_child *j = j_children; 
  852.  
  853.     if((flags & jobflg) == 0)
  854.         return FALSE;
  855.  
  856.     await(-2, 1);
  857.     for(; j < &j_children[NJCH]; j++)
  858.         if(j->j_status == JSTOP )
  859.             if(force)  {
  860.                 killpg(j->j_pgid, SIGHUP);
  861.                 killpg(j->j_pgid, SIGCONT);
  862.             }
  863.             else  {
  864.                 prs(tasjstr);    /* DAG */
  865.                 return TRUE;
  866.             }
  867.     if(force)  {
  868.         await(-2, 1);        
  869.         return FALSE;
  870.     }
  871.     trapflg[SIGTTIN] = SIGMOD;
  872.     trapflg[SIGTTOU] = SIGMOD;
  873.     trapflg[SIGTSTP] = SIGMOD;
  874.     trapflg[SIGSTOP] = SIGMOD;
  875.     flags &= ~jobflg;
  876.     j_really_reset_pg();
  877.     return FALSE;
  878. }
  879.  
  880.  
  881. static int    j_numbers = 0;
  882.  
  883. static int
  884. j_getnumber()
  885. {
  886.     register struct j_child *j = j_children; 
  887.     
  888.     for(; j < &j_children[NJCH]; j++)
  889.         if(j->j_status != JEMPTY && j->j_jobnum)
  890.             return j_numbers++;
  891.     j_numbers = 2;
  892.     return 1;
  893. }
  894.  
  895. static void
  896. j_backoff()
  897. {
  898.     register struct j_child *j = j_children; 
  899.     
  900.     j_current = 0;
  901.     for(; j < &j_children[NJCH]; j++)
  902.         if(j->j_status != JEMPTY && j->j_jobnum)
  903.             if(j->j_jobnum > j_current)
  904.                 j_current = j->j_jobnum;
  905. }
  906.  
  907. static    int    jcleft;
  908. static  char    *jcp;
  909.  
  910. static void
  911. j_setcommand(j, t)
  912.     register struct j_child    *j;
  913.     struct trenod        *t;
  914. {
  915.     jcleft = strlen(j->j_com);
  916.     jcp = j->j_com + jcleft;
  917.     jcleft = JCOMSIZE - 1  - jcleft;
  918.  
  919.     if(j->j_com[0] == '\0' || !j_stuff(pipestr))    /* DAG */
  920.         j_do(t);
  921. }
  922.  
  923. static int
  924. j_do_chain(a)
  925.     register struct argnod *a;
  926. {
  927.     while(a)  {
  928.         if(j_stuff(a->argval))
  929.             return 1;
  930.         a = a->argnxt;
  931.         if(a)
  932.             j_stuff(spcstr);    /* DAG */
  933.     }
  934.     return 0;
  935. }
  936.  
  937. #define IOGET 0
  938. static int
  939. j_do_redir(t)
  940.     register struct ionod    *t;
  941. {
  942.     register int    iof;    /* DAG -- added for speed */
  943.     register int    i;
  944.  
  945.     while(t)  {
  946.         if(t->ioname)  {
  947.             if(j_stuff(spcstr))    /* DAG */
  948.                 return 1;
  949.             iof = t->iofile;
  950.             i = iof & IOUFD;
  951.             if(
  952.                 ((iof&IOPUT) && (i != 1)) ||
  953.                 (((iof&IOPUT)==0) && (i!= 0))
  954.             )  {
  955.                 itos(i);
  956.                 if(j_stuff(numbuf))
  957.                     return 1;
  958.             }
  959.             switch(iof & (IODOC|IOPUT|IOMOV|IOAPP|IORDW))  {
  960.             case IOGET:
  961.                 if(j_stuff(rdinstr))    /* DAG */
  962.                     return 1;
  963.                 break;
  964.             case IOPUT:
  965.                 if(j_stuff(readmsg))    /* DAG */
  966.                     return 1;
  967.                 break;
  968.             case IOAPP|IOPUT:
  969.                 if(j_stuff(appdstr))    /* DAG */
  970.                     return 1;
  971.                 break;
  972.             case IODOC:
  973.                 if(j_stuff(inlnstr))    /* DAG */
  974.                     return 1;
  975.                 break;
  976.             case IOMOV|IOPUT:
  977.                 if(j_stuff(toastr))    /* DAG */
  978.                     return 1;
  979.                 break; 
  980.             case IOMOV|IOGET:
  981.                 if(j_stuff(fromastr))    /* DAG */
  982.                     return 1;
  983.                 break;
  984.             case IORDW:
  985.                 if(j_stuff(rdwstr))    /* ADR */
  986.                     return 1;
  987.                 break;
  988.             }
  989.             if(j_stuff(t->ioname))
  990.                 return 1;
  991.         }
  992.         t = t->ionxt;
  993.     }
  994.     return 0;
  995. }
  996.                 
  997.                 
  998. static int
  999. j_do(t)    
  1000.     register struct trenod *t;
  1001. {
  1002.     int    type;
  1003.  
  1004.     if (t == (struct trenod *)0)    /* DAG -- added safety check */
  1005.         return 0;
  1006.  
  1007.     type = t->tretyp & COMMSK;
  1008.     switch(type)  {
  1009. #ifdef TFND    /* ADR --- don't put this stuff in the plain BSD /bin/sh */
  1010.     case    TFND:    /* added by DAG for System V Release 2 shell */
  1011.         return j_stuff(fndptr(t)->fndnam)
  1012.             || j_stuff(sfnstr)    /* DAG */
  1013.             || j_do(fndptr(t)->fndval)
  1014.             || j_stuff(efnstr);    /* DAG */
  1015. #endif
  1016.  
  1017.     case    TCOM:
  1018.         if(comptr(t)->comset)  {
  1019.             if(j_do_chain(comptr(t)->comset)
  1020.             || j_stuff(spcstr))
  1021.                 return 1;            
  1022.         }
  1023.         return j_do_chain(comptr(t)->comarg)
  1024.             || j_do_redir(comptr(t)->comio);
  1025.  
  1026.     case    TLST:
  1027.     case    TAND:
  1028.     case    TORF:
  1029.     case    TFIL:    /* DAG -- merged */
  1030.         if(j_do(lstptr(t)->lstlef))
  1031.             return 1;
  1032.         switch(type)  {
  1033.         case TLST:
  1034.             if(j_stuff(semspstr))    /* DAG */
  1035.                 return 1;
  1036.             break;
  1037.         case TAND:
  1038.             if(j_stuff(andstr))    /* DAG */
  1039.                 return 1;
  1040.             break;
  1041.         case TORF:
  1042.             if(j_stuff(orstr))    /* DAG */
  1043.                 return 1;
  1044.             break;
  1045.         case TFIL:
  1046.             if(j_stuff(pipestr))    /* DAG */
  1047.                 return 1;
  1048.             break;
  1049.         }
  1050.         return j_do(lstptr(t)->lstrit);
  1051.  
  1052.     case    TFORK:
  1053.         return j_do(forkptr(t)->forktre)
  1054.             || j_do_redir(forkptr(t)->forkio)
  1055.             || (forkptr(t)->forktyp & FAMP) && j_stuff(amperstr);    /* DAG */
  1056.  
  1057.     case    TPAR:
  1058.         return j_stuff(lpnstr)    /* DAG */
  1059.             || j_do(parptr(t)->partre)
  1060.             || j_stuff(rpnstr);    /* DAG */
  1061.  
  1062.     case    TFOR:
  1063.     case    TWH:
  1064.     case    TUN:
  1065.     {
  1066.         struct trenod *c;
  1067.  
  1068.         switch(type)  {
  1069.         case TFOR:
  1070.             if(j_stuff(forstr)    /* DAG */
  1071.             || j_stuff(forptr(t)->fornam))
  1072.                 return 1;
  1073.             if(forptr(t)->forlst)  {
  1074.                 if(j_stuff(insstr)    /* DAG */
  1075.                 || j_do_chain(forptr(t)->forlst->comarg))
  1076.                     return 1;
  1077.             }
  1078.             c = forptr(t)->fortre;
  1079.             break;
  1080.  
  1081.         case TWH:
  1082.             if(j_stuff(whilestr)    /* DAG */
  1083.             || j_do(whptr(t)->whtre))
  1084.                 return 1;
  1085.             c = whptr(t)->dotre;
  1086.             break;
  1087.  
  1088.         case TUN:
  1089.             if(j_stuff(untilstr)    /* DAG */
  1090.             || j_do(whptr(t)->whtre))
  1091.                 return 1;
  1092.             c = whptr(t)->dotre;
  1093.             break;
  1094.         }
  1095.         return j_stuff(sdostr)    /* DAG */
  1096.             || j_do(c)
  1097.             || j_stuff(sdonstr);    /* DAG */
  1098.     }
  1099.     case    TIF:
  1100.         if(j_stuff(ifstr)    /* DAG */
  1101.         || j_do(ifptr(t)->iftre)
  1102.         || j_stuff(sthnstr)    /* DAG */
  1103.         || j_do(ifptr(t)->thtre))
  1104.             return 1;
  1105.         if(ifptr(t)->eltre)  {
  1106.             if(j_stuff(selsstr)    /* DAG */
  1107.             || j_do(ifptr(t)->eltre))
  1108.                 return 1;
  1109.         }
  1110.         return j_stuff(sfistr);    /* DAG -- bug fix (was "; done") */
  1111.  
  1112.     case    TSW:
  1113.         return j_stuff(casestr)    /* DAG */
  1114.             || j_stuff(swptr(t)->swarg)
  1115.             || j_stuff(iesacstr);    /* DAG */
  1116.  
  1117.     default:
  1118. /*        printf("sh bug: j_do--unknown type %d\n", type);    */
  1119.         return 0;
  1120.     }
  1121. }
  1122.  
  1123. static int
  1124. j_stuff(f)
  1125.     char    *f;
  1126. {
  1127.     register int    i;
  1128.     register int    runover;
  1129.  
  1130.     i = strlen(f);
  1131.     runover = i > jcleft;
  1132.     if(runover)
  1133.         i = jcleft;
  1134.     strncpy(jcp, f, i);
  1135.     jcleft -= i;
  1136.     jcp += i;
  1137.     *jcp = 0;
  1138.     if(runover)  {
  1139.         jcp[-1] = '.';
  1140.         jcp[-2] = '.';
  1141.         jcp[-3] = '.';
  1142.     }
  1143.     return runover;
  1144. }
  1145. SHAR_EOF
  1146. echo shar: extracting "'homedir.c'" '(3036 characters)'
  1147. if test -f 'homedir.c'
  1148. then
  1149.     echo shar: over-writing existing file "'homedir.c'"
  1150. fi
  1151. cat << \SHAR_EOF > 'homedir.c'
  1152. /*
  1153.  * homedir.c
  1154.  *
  1155.  * find a person's login directory, for use by the shell
  1156.  * also find the current user's login name.
  1157.  *
  1158.  * Arnold Robbins
  1159.  */
  1160.  
  1161. #include "defs.h"
  1162.  
  1163. /* validtilde --- indicate whether or not a ~ is valid */
  1164.  
  1165. int validtilde (start, argp)
  1166. register char *start, *argp;
  1167. {
  1168.     return (
  1169.     start == argp - 1 ||            /* ~ at beginning of argument */
  1170.     argp[-2] == '=' ||            /* ~ after an assignment */
  1171.     (*start == '-' && argp - 3 == start)    /* in middle of an option */
  1172.                         /* CSH does not do that one */
  1173.     );
  1174. }
  1175.  
  1176. /* homedir --- return the person's login directory */
  1177.  
  1178. char *homedir (person)
  1179. register char *person;
  1180. {
  1181.     register int count, i, j, fd;
  1182.     static char dir[150];
  1183.     char buf[300], name[100], rest[100];
  1184.  
  1185.     if (person[0] == '\0')    /* just a plain ~ */
  1186.         return (homenod.namval);
  1187.     else if (person[0] == '/')    /* e.g. ~/bin */
  1188.     {
  1189.         /* sprintf (dir, "%s%s", homenod.namval, person); */
  1190.         movstr (movstr (homenod.namval, dir), person);
  1191.         return (dir);
  1192.     }
  1193.  
  1194.     if ((fd = open ("/etc/passwd", 0)) < 0)
  1195.         return (nullstr);
  1196.     
  1197.     /*
  1198.      * this stuff is to handle the ~person/bin sort of thing
  1199.      * for catpath()
  1200.      */
  1201.     movstr (person, name);
  1202.     *rest = '\0';
  1203.     for (i = 0; person[i]; i++)
  1204.         if (person[i] == '/')
  1205.         {
  1206.             movstr (& person[i], rest);
  1207.             name[i] = '\0';
  1208.             break;
  1209.         }
  1210.  
  1211.     while ((count = read (fd, buf, sizeof(buf))) > 0)
  1212.     {
  1213.         for (i = 0; i < count; i++)
  1214.             if (buf[i] == '\n')
  1215.             {
  1216.                 i++;
  1217.                 lseek (fd, (long) (- (count - i)), 1);
  1218.                 break;
  1219.             }
  1220.         buf[i] = '\0';
  1221.         for (j = 0; name[j] && buf[j] == name[j]; j++)
  1222.             ;
  1223.         if (buf[j] == ':' && name[j] == '\0')
  1224.             break;    /* found it */
  1225.     }
  1226.     if (count == 0)
  1227.     {
  1228.         close (fd);
  1229.         return (nullstr);
  1230.     }
  1231.  
  1232.     j--;
  1233.     for (i = 1; i <= 5; i++)
  1234.     {
  1235.         for (; buf[j] != ':'; j++)
  1236.             ;
  1237.         j++;
  1238.     }
  1239.     for (i = 0; buf[j] != ':'; i++, j++)
  1240.         dir[i] = buf[j];
  1241.     if (rest[0])
  1242.         for (j = 0; rest[j]; j++)
  1243.             dir[i++] = rest[j];
  1244.     dir[i] = '\0';
  1245.     close (fd);
  1246.     return (dir);
  1247. }
  1248.  
  1249. /* username --- return the user's login name */
  1250.  
  1251. /*
  1252.  * this routine returns the first user name in /etc/passwd that matches the
  1253.  * real uid.  This could be a problem on some systems, but we don't want to
  1254.  * call getlogin(), since it uses stdio, and the shell does not.
  1255.  */
  1256.  
  1257. char *username ()
  1258. {
  1259.     register int count, i, j, fd;
  1260.     static char logname[50];
  1261.     static int foundname = FALSE;
  1262.     char buf[300];
  1263.  
  1264.     if (foundname)
  1265.         return (logname);
  1266.  
  1267.     if ((fd = open ("/etc/passwd", 0)) < 0)
  1268.         return (nullstr);
  1269.     
  1270.     itos (getuid());
  1271.     while ((count = read (fd, buf, sizeof(buf))) > 0)
  1272.     {
  1273.         for (i = 0; i < count; i++)
  1274.             if (buf[i] == '\n')
  1275.             {
  1276.                 i++;
  1277.                 lseek (fd, (long) (- (count - i)), 1);
  1278.                 break;
  1279.             }
  1280.         buf[i] = '\0';
  1281.         for (j = 0, i = 1; i <= 2; i++)
  1282.         {
  1283.             for (; buf[j] != ':'; j++)
  1284.                 ;    /* skip name && passwd */
  1285.             j++;
  1286.         }
  1287.             
  1288.         for (i = 0; numbuf[i] && buf[j] == numbuf[i]; i++, j++)
  1289.             ;
  1290.         if (buf[j] == ':' && numbuf[i] == '\0')
  1291.             break;    /* found it */
  1292.     }
  1293.     if (count == 0)
  1294.     {
  1295.         close (fd);
  1296.         return (nullstr);
  1297.     }
  1298.  
  1299.     for (i = 0; buf[i] != ':'; i++)
  1300.         logname[i] = buf[i];
  1301.     logname[i] = '\0';
  1302.     foundname = TRUE;
  1303.     close (fd);
  1304.     return (logname);
  1305. }
  1306. SHAR_EOF
  1307. echo shar: extracting "'history.c'" '(23486 characters)'
  1308. if test -f 'history.c'
  1309. then
  1310.     echo shar: over-writing existing file "'history.c'"
  1311. fi
  1312. cat << \SHAR_EOF > 'history.c'
  1313. /* history.c --- interacterive history mechanism for the Bourne shell */
  1314.  
  1315. /*
  1316.  * Original design by Jeff Lee for the Software Tools Subsystem,
  1317.  * This implementation by Arnold Robbins, based on Jeff's, but
  1318.  * a little bit more capable.
  1319.  */
  1320.  
  1321. #include "defs.h"    /* defines HISTSIZE */
  1322. #include "sym.h"
  1323.  
  1324. #define MAXHIST        256        /* max no. saved commands */
  1325. #define MAXLINE        257
  1326. #define BIGBUF        (MAXLINE * 2)
  1327.  
  1328. #define HISTCHAR    '!'        /* history flag character */
  1329. #define HISTLOOK    '?'        /* history global search command */
  1330. #define HISTARG        '`'        /* history argument character */
  1331. #define HISTSUB        '^'        /* history substitution character */
  1332.  
  1333. #define YES    (1)
  1334. #define NO    (0)
  1335.  
  1336. #ifndef TAB    /* earlier version of the shell */
  1337. #define    TAB    '\t'
  1338. #endif
  1339.  
  1340. static char    Histbuf[HISTSIZE];    /* queue holding actual history */
  1341. static int    Histptr[MAXHIST];    /* queue of pointers into buffer */
  1342. static int    Hbuffirst = 0;        /* First pointer into Histbuf */
  1343. static int    Hbuflast = 0;        /* Last pointer into Histbuf */
  1344. static int    Hptrfirst = 0;        /* First pointer into Histptr */
  1345. static int    Hptrlast = 0;        /* Last pointer into Histptr */
  1346. static int    Histline = 0;        /* no. of cmd pointed to by Histptr[Hptrlast] */
  1347.  
  1348. static char h_badopt[] =    " unrecognized history option";
  1349. static char badarg[] =    " illegal argument history";
  1350. static char nohist[] =    " no history exists, yet";
  1351. static char h_illegal[] =    " illegal history construct";
  1352. static char bufover[] =    " history buffer overflow";
  1353. static char bigtok[] =    " history token too large";
  1354. static char internal[] =    " history internal error";
  1355. static char badtoken[] =    " illegal history token";
  1356. static char h_notfound[] =    " history item not found";
  1357. static char bigexp[] =    " history expansion too big";
  1358.  
  1359. extern int    histsub ();        /* do a history substitution */
  1360. static void    histinit ();        /* reinitialize history mechanism */
  1361. static int    histexp ();        /* do a history expansion */
  1362. static int    histque ();        /* save a command in the buffers */
  1363. static void    histfree ();        /* free up some buffer storage */
  1364. static int    histfind ();        /* find a history command */
  1365. static int    histlook ();        /* get a previous command */
  1366. static int    histget ();        /* get a string from the buffers */
  1367. static void    histarg ();        /* get individual arguments */
  1368. extern int    histrest ();        /* restore saved history */
  1369. extern int    histsave ();        /* save current history */
  1370.  
  1371. static int    Bquote = 0;        /* count grave accents */
  1372. static int    Dquote = 0;        /* count single quotes */
  1373. static int    Squote = 0;        /* count double quotes */
  1374.  
  1375. #define errmsg(x, s)    { prs(x); prc(COLON); prs(s); newline(); return (FALSE); }
  1376.  
  1377. #define repeat        do    /* repeat ... until is easier to read */
  1378. #define until(cond)    while (!(cond))
  1379.  
  1380. /* histsub --- perform a history substitution */
  1381.  
  1382. int histsub (in, out, outsize)
  1383. char *in, *out;
  1384. int outsize;
  1385. {
  1386.     if ((flags&prompt) == 0 || in == 0 || *in == '\0')
  1387.         return (TRUE);    /* no history, pretend all ok */
  1388.     
  1389.     return (histexp (in, out, outsize) && histque (out));
  1390. }
  1391.  
  1392. /* histexp --- perform history expansion on a command line */
  1393.  
  1394. static int histexp (in, out, outsize)
  1395. char *in, *out;
  1396. int outsize;
  1397. {
  1398.     int i;
  1399.     int istart, ilen, ostart;
  1400.     char buf[MAXLINE], result[BIGBUF];
  1401.     auto int bangseen = NO;
  1402.  
  1403.     if (in[0] == NL || in[0] == '\0')
  1404.         return (FALSE);
  1405.  
  1406.     istart = ostart = ilen = 0;
  1407.     while (in[istart] && in[istart] != HISTCHAR)
  1408.     {
  1409.         if (ostart >= outsize)
  1410.             errmsg (in, bigexp);
  1411.         switch (in[istart]) {
  1412.         case ESCAPE:
  1413.             out[ostart++] = in[istart++];
  1414.             if (in[istart] == HISTCHAR)
  1415.             {
  1416.                 bangseen = YES;
  1417.                 if (Squote)
  1418.                     out[ostart++] = in[istart++];
  1419.                 else
  1420.                     out[ostart - 1] = in[istart++];
  1421.                     /* no quotes, nuke \ */
  1422.                 continue;
  1423.             }
  1424.             break;
  1425.         case '`':
  1426.             if (Dquote == 0 && Squote == 0)
  1427.                 Bquote = 1 - Bquote;
  1428.             break;
  1429.         case '\'':
  1430.             if (Bquote == 0 && Dquote == 0)
  1431.                 Squote = 1 - Squote;
  1432.             break;
  1433.         case '"':
  1434.             if (Bquote == 0 && Squote == 0)
  1435.                 Dquote = 1 - Dquote;
  1436.             break;
  1437.         }
  1438.         if (ostart >= outsize)
  1439.             errmsg (in, bigexp);
  1440.         out[ostart++] = in[istart++];
  1441.         if (Squote && in[istart] == HISTCHAR)
  1442.             if (ostart >= outsize)
  1443.             {
  1444.                 errmsg (in, bigexp);
  1445.             }
  1446.             else
  1447.                 out[ostart++] = in[istart++];
  1448.     }
  1449.  
  1450.     if (in[istart] == '\0')
  1451.     {
  1452.         out[ostart] = '\0';
  1453.         if (bangseen)
  1454.             expanded = YES;        /* see comment below */
  1455.         return (TRUE);    /* no history to do */
  1456.     }
  1457.  
  1458.     expanded = NO;    /* this is a global flag */
  1459.     while (histfind (in, &istart, &ilen))    /* we found something to do */
  1460.     {
  1461.         if (ilen >= MAXLINE)
  1462.             errmsg (&in[istart], bigtok);
  1463.  
  1464.         /* save the history part */
  1465.         strncpy (buf, & in[istart], ilen);
  1466.         buf[ilen] = '\0';
  1467.         istart += ilen;
  1468.         if (buf[ilen-1] == HISTCHAR)
  1469.             buf[--ilen] = '\0';
  1470.         
  1471.         /* actually make the substitution */
  1472.         if (! histlook (buf, result))
  1473.             return (FALSE);
  1474.         
  1475.         /* put it into generated line */
  1476.         i = length (result) - 2;
  1477.         if (result[i] == NL)
  1478.             result[i] = '\0';
  1479.         if (ostart + i + 1 >= outsize)
  1480.             errmsg (&in[istart], bigexp);
  1481.         movstr (result, & out[ostart]);
  1482.         ostart += length (result) - 1;
  1483.         expanded = YES;
  1484.         while (in[istart] && in[istart] != HISTCHAR)
  1485.         {
  1486.             if (ostart >= outsize)
  1487.                 errmsg (&in[istart], bigexp);
  1488.             switch (in[istart]) {
  1489.             case ESCAPE:
  1490.                 out[ostart++] = in[istart++];
  1491.                 if (in[istart] == HISTCHAR)
  1492.                 {
  1493.                     bangseen = YES;
  1494.                     if (Squote)
  1495.                         out[ostart++] = in[istart++];
  1496.                     else
  1497.                         out[ostart - 1] = in[istart++];
  1498.                         /* no quotes, nuke \ */
  1499.                     continue;
  1500.                 }
  1501.                 break;
  1502.             case '`':
  1503.                 if (Dquote == 0 && Squote == 0)
  1504.                     Bquote = 1 - Bquote;
  1505.                 break;
  1506.             case '\'':
  1507.                 if (Bquote == 0 && Dquote == 0)
  1508.                     Squote = 1 - Squote;
  1509.                 break;
  1510.             case '"':
  1511.                 if (Bquote == 0 && Squote == 0)
  1512.                     Dquote = 1 - Dquote;
  1513.                 break;
  1514.             }
  1515.             if (ostart >= outsize)
  1516.                 errmsg (&in[istart], bigexp);
  1517.             out[ostart++] = in[istart++];
  1518.             if (Squote && in[istart] == HISTCHAR)
  1519.                 if (ostart >= outsize)
  1520.                 {
  1521.                     errmsg (&in[istart], bigexp);
  1522.                 }
  1523.                 else
  1524.                     out[ostart++] = in[istart++];
  1525.         }
  1526.     }
  1527.  
  1528.     out[ostart] = '\0';
  1529.  
  1530.     if (expanded)
  1531.         prs (out);    /* should contain newline */
  1532.     else if (bangseen)
  1533.         expanded = YES;
  1534.  
  1535.     /*
  1536.      * This is a KLUDGE, so that escaped !s work;
  1537.      * it depends on knowledge of how readb() in word.c
  1538.      * works, i.e., if expanded, use the generated buffer.
  1539.      * This way, only expanded is needed as a global variable.
  1540.      */
  1541.  
  1542.     return (TRUE);
  1543. }
  1544.  
  1545. /* histque --- place the given command in the history queue */
  1546.  
  1547. static int histque (command)
  1548. char *command;
  1549. {
  1550.     int c;
  1551.     char *p;
  1552.     static int Inaquote = FALSE;    /* in a quote across commands */
  1553.  
  1554.     for (; *command && (*command == SP || *command == TAB); command++)
  1555.         ; /* skip leading white space */
  1556.  
  1557.     if (*command == NL && *(command+1) == '\0')
  1558.         return (TRUE);    /* don't queue empty commands */
  1559.                 /* or increment event_count */
  1560.  
  1561.     if (Inaquote)
  1562.     {
  1563.         /* clobber trailing \0 */
  1564.         if (Hbuffirst == 0)
  1565.             Hbuffirst = HISTSIZE - 1;
  1566.         else
  1567.             Hbuffirst--;
  1568.  
  1569.         event_count--;
  1570.     }
  1571.  
  1572.     Histptr[Hptrfirst] = Hbuffirst;
  1573.     if (! Inaquote)
  1574.         Hptrfirst = (Hptrfirst + 1) % MAXHIST;
  1575.  
  1576.     if (Hptrfirst == Hptrlast)
  1577.         histfree ();
  1578.  
  1579.     p = command;
  1580.     c = *p++;
  1581.     while (c != '\0' && Hptrfirst != Hptrlast)
  1582.     {
  1583.         repeat
  1584.         {
  1585.             Histbuf[Hbuffirst] = c;
  1586.             c = *p++;
  1587.             Hbuffirst = (Hbuffirst + 1) % HISTSIZE;
  1588.         } until (c == '\0' || Hbuffirst == Hbuflast);
  1589.  
  1590.         if (Hbuffirst == Hbuflast)
  1591.             histfree ();
  1592.     }
  1593.  
  1594.     if (Hptrfirst != Hptrlast)
  1595.     {
  1596.         Histbuf[Hbuffirst] = '\0';
  1597.         Hbuffirst = (Hbuffirst + 1) % HISTSIZE;
  1598.  
  1599.         if (Hbuffirst == Hbuflast)
  1600.             histfree ();
  1601.     }
  1602.  
  1603.     Inaquote = (Bquote || Dquote || Squote);
  1604.  
  1605.     if (Hptrfirst == Hptrlast)
  1606.     {
  1607.         histinit ();
  1608.         errmsg (nullstr, bufover);
  1609.         /* errmsg returns FALSE */
  1610.     }
  1611.  
  1612.     event_count++;
  1613.     return (TRUE);
  1614. }
  1615.  
  1616. /* histfree --- free the next queue pointer */
  1617.  
  1618. static void histfree ()
  1619. {
  1620.     Hptrlast = (Hptrlast + 1) % MAXHIST;
  1621.  
  1622.     Hbuflast = Histptr[Hptrlast];
  1623.     Histline++;
  1624. }
  1625.  
  1626. /* histfind --- find the start and length of a history pattern */
  1627.  
  1628. static int histfind (command, start, len)
  1629. char *command;
  1630. int *start, *len;
  1631. {
  1632.     char *p, c;
  1633.     int subseen;
  1634.  
  1635.     p = command + *start;
  1636.     c = *p++;
  1637.  
  1638.     *len = 0;
  1639.     if (c == NL || c == '\0')
  1640.         return (FALSE);
  1641.  
  1642.     /* skip leading non-history */
  1643.     while (c && c != HISTCHAR)
  1644.     {
  1645.         if (c == ESCAPE)
  1646.         {
  1647.             c = *p++;
  1648.             *start += 1;
  1649.         }
  1650.  
  1651.         if (c != '\0')
  1652.         {
  1653.             c = *p++;
  1654.             *start += 1;
  1655.         }
  1656.     }
  1657.  
  1658.     if (c == NL || c == '\0')
  1659.         return (FALSE);
  1660.     
  1661.     *len = 1;
  1662.     c = *p++;
  1663.     if (c == HISTLOOK)    /* !?...? */
  1664.     {
  1665.         *len += 1;
  1666.         c = *p++;
  1667.         while (c && c != HISTLOOK && c != NL)
  1668.         {
  1669.             if (c == ESCAPE)
  1670.             {
  1671.                 c = *p++;
  1672.                 *len += 1;
  1673.             }
  1674.  
  1675.             if (c != '\0')
  1676.             {
  1677.                 c = *p++;
  1678.                 *len += 1;
  1679.             }
  1680.         }
  1681.         if (c == HISTLOOK)
  1682.         {
  1683.             c = *p++;
  1684.             *len += 1;
  1685.         }
  1686.     }
  1687.     else if (digit (c) || c == '-')    /* !<num> */
  1688.     {
  1689.         if (c == '-')
  1690.         {
  1691.             c = *p++;
  1692.             *len += 1;
  1693.  
  1694.             if (! digit(c))
  1695.                 errmsg (command + *start, h_illegal);
  1696.         }
  1697.  
  1698.         while (digit (c))
  1699.         {
  1700.             c = *p++;
  1701.             *len += 1;
  1702.         }
  1703.     }
  1704.     else    /* !<str> */
  1705.         while (c && c != HISTARG && c != HISTSUB && c != SP &&
  1706.                 c != TAB && c != NL && c != HISTCHAR)
  1707.         {
  1708.             if (c == ESCAPE)
  1709.             {
  1710.                 c = *p++;
  1711.                 *len += 1;
  1712.             }
  1713.  
  1714.             if (c != '\0')
  1715.             {
  1716.                 c = *p++;
  1717.                 *len += 1;
  1718.             }
  1719.         }
  1720.  
  1721.     if (c == HISTARG)
  1722.     {
  1723.         *len += 1;
  1724.         c = *p++;
  1725.         while (c && digit (c))
  1726.         {
  1727.             *len += 1;
  1728.             c = *p++;
  1729.         }
  1730.         if (c == '-')
  1731.         {
  1732.             *len += 1;
  1733.             c = *p++;
  1734.         }
  1735.         if (c == '$')
  1736.         {
  1737.             *len += 1;
  1738.             c = *p++;
  1739.         }
  1740.         else
  1741.         {
  1742.             while (c && digit (c))
  1743.             {
  1744.                 *len += 1;
  1745.                 c = *p++;
  1746.             }
  1747.         }
  1748.     }
  1749.  
  1750.     while (c == HISTSUB)
  1751.     {
  1752.         *len += 1;
  1753.         subseen = 0;
  1754.         c = *p++;
  1755.  
  1756.         while (subseen < 2 && c != NL && c != '\0')
  1757.         {
  1758.             if (c == ESCAPE)
  1759.             {
  1760.                 c = *p++;
  1761.                 *len += 1;
  1762.             }
  1763.  
  1764.             if (c != '\0')
  1765.             {
  1766.                 c = *p++;
  1767.                 *len += 1;
  1768.             }
  1769.  
  1770.             if (c == HISTSUB)
  1771.                 subseen++;
  1772.         }
  1773.  
  1774.         if (c == HISTSUB)
  1775.         {
  1776.             *len += 1;
  1777.             c = *p++;
  1778.             if (c == 'g' || c == 'G')
  1779.             {
  1780.                 *len += 1;
  1781.                 c = *p++;
  1782.             }
  1783.         }
  1784.     }
  1785.  
  1786.     if (c == HISTCHAR)
  1787.         *len += 1;
  1788.     
  1789.     return (TRUE);
  1790. }
  1791.  
  1792. /* histlook --- lookup the value of a history string */
  1793.  
  1794. static int histlook (str, sub)
  1795. char *str, *sub;
  1796. {
  1797.     char c;
  1798.     char *save, *p, *sp;
  1799.     char buf[BIGBUF], rep[BIGBUF];
  1800.     char new[BIGBUF];
  1801.     int i, j, val, si, flag, len, last;
  1802.     static int ctoi();
  1803.  
  1804.     save = sub;
  1805.     /*
  1806.      * first attempt to find which command on which we are to operate
  1807.      *
  1808.      * the entire hstory format is as follows
  1809.      *
  1810.      * ! [<str> | <num> | ?<str>?] [`<num> [- [<num>]]] {^<str>^<str>^ [g]}
  1811.      */
  1812.     
  1813.     si = 0;
  1814.  
  1815.     if (str[si] == HISTCHAR)
  1816.         si++;
  1817.  
  1818.     switch (str[si]) {
  1819.     case '\0':    /* ! */
  1820.     case NL:
  1821.     case HISTARG:    /* on these, retrive previous line, then break */
  1822.     case HISTSUB:
  1823.         if (Hptrfirst == Hptrlast)
  1824.             errmsg (nullstr, nohist);
  1825.  
  1826.         val = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1;
  1827.         if (! histget (val, sub))
  1828.             errmsg (nullstr, internal);
  1829.         break;
  1830.  
  1831.     case '-':
  1832.     case '0':    /* !<num> */
  1833.     case '1':
  1834.     case '2':
  1835.     case '3':
  1836.     case '4':
  1837.     case '5':
  1838.     case '6':
  1839.     case '7':
  1840.     case '8':
  1841.     case '9':
  1842.         val = ctoi (str, &si) - 1;    /* for 0-based indexing */
  1843.         if (! histget(val, sub))
  1844.             errmsg (str, h_notfound);
  1845.         break;
  1846.     
  1847.     case HISTLOOK:    /* ?<str>? */
  1848.         i = 0;
  1849.         si++;
  1850.         while (str[si] && str[si] != HISTLOOK)
  1851.         {
  1852.             if (str[si] == ESCAPE)
  1853.                 si++;
  1854.             
  1855.             if (str[si])
  1856.                 buf[i++] = str[si++];
  1857.         }
  1858.         buf [i] = '\0';
  1859.         if (str[si] == HISTLOOK)
  1860.             si++;
  1861.         if (buf[i-1] == NL)
  1862.             buf[--i] = '\0';
  1863.         
  1864.         flag = FALSE;
  1865.         val = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1;
  1866.         *sub = '\0';
  1867.         while (histget (val, sub))
  1868.         {
  1869.             p = sub;
  1870.             c = *p++;
  1871.             while (c)
  1872.             {
  1873.                 i = 0;
  1874.                 while (c != '\0' && buf[i] != '\0' && c != buf[i])
  1875.                 {
  1876.                     /* skip non matching */
  1877.                     c = *p++;
  1878.                     if (*p == '\0')
  1879.                         break;
  1880.                 }
  1881.                 
  1882.                 sp = p;
  1883.  
  1884.                 while (c && buf[i] && c == buf[i])
  1885.                 {
  1886.                     /* possibly matching */
  1887.                     c = *p++;
  1888.                     i++;
  1889.                 }
  1890.  
  1891.                 if (buf[i] == '\0')
  1892.                 {
  1893.                     /* did match */
  1894.                     flag = TRUE;
  1895.                     goto out;
  1896.                 }
  1897.  
  1898.                 p = sp;
  1899.                 c = *p++;
  1900.             }
  1901.             val--;    /* search further back, next time around */
  1902.             *sub = '\0';
  1903.         }
  1904.  
  1905.     out:
  1906.         if (flag == FALSE)
  1907.             errmsg (str, h_notfound);
  1908.         break;
  1909.  
  1910.     default:    /* !<str> */
  1911.         i = 0;
  1912.         while (str[si] && str[si] != HISTARG && str[si] != HISTSUB)
  1913.         {
  1914.             if (str[si] == ESCAPE)
  1915.                 si++;
  1916.             
  1917.             if (str[si])
  1918.                 buf[i++] = str[si++];
  1919.         }
  1920.         buf[i] = '\0';
  1921.  
  1922.         flag = FALSE;
  1923.         val = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1;
  1924.         while (histget (val, sub))
  1925.         {
  1926.             p = sub;
  1927.             c = *p++;
  1928.             while (c == SP || c == TAB)
  1929.                 c = *p++;
  1930.  
  1931.             i = 0;
  1932.             while (buf[i] && buf[i] == c)
  1933.             {
  1934.                 c = *p++;
  1935.                 i++;
  1936.             }
  1937.  
  1938.             if (buf[i] == '\0')
  1939.             {
  1940.                 flag = TRUE;    /* found it */
  1941.                 break;    /* while */
  1942.             }
  1943.             val--;
  1944.         }
  1945.         if (flag == FALSE)
  1946.             errmsg (str, h_notfound);
  1947.         break;
  1948.     } /* end switch */
  1949.  
  1950.     j = length (sub) - 2;
  1951.     if (sub[j] == NL)
  1952.         sub[j] = '\0';
  1953.  
  1954.     /*
  1955.      * ! [<str> | <num> | ? <str> ?] has now been parsed and the command
  1956.      * line has been placed in "sub". Now see if the next character is a
  1957.      * legal following character
  1958.      */
  1959.     
  1960.     if (str[si] && str[si] != HISTARG && str[si] != HISTSUB && str[si] != NL)
  1961.         errmsg (str, badtoken);
  1962.  
  1963.     /* if there is no more to the history string, we are done */
  1964.     if (str[si] == NL || str[si] == '\0')
  1965.         return (TRUE);
  1966.     
  1967.     /*
  1968.      * now check for possible argument substitution. This section parses
  1969.      * [` <num>] and turns "sub" into the appropriate argument
  1970.      */
  1971.     
  1972.     if (str[si] == HISTARG)        /* `<num>-<num> */
  1973.     {
  1974.         si++;
  1975.  
  1976.         if (! digit(str[si]) && str[si] != '-' && str[si] != '$')
  1977.             errmsg (str, badarg);
  1978.         
  1979.         /* determine the last argument */
  1980.         p = sub;
  1981.         last = -1;
  1982.         /* count arguments, last will be val of $ */
  1983.         histarg (p, &len);
  1984.         while (len > 0)
  1985.         {
  1986.             last++;
  1987.             p += len;
  1988.             histarg (p, &len);
  1989.         }
  1990.  
  1991.         if (str[si] == '-')    /* default to arg 1 */
  1992.             val = 1;
  1993.         else if (digit(str[si]))
  1994.             val = min (ctoi(str, &si), last + 1);
  1995.         else
  1996.         {
  1997.             /* $ */
  1998.             val = last;
  1999.  
  2000.             if (str[si] != '$')
  2001.             {
  2002.                 errmsg (str, internal);
  2003.             }
  2004.             else
  2005.                 si++;
  2006.         }
  2007.  
  2008.         p = sub;
  2009.         for (i = val; i > 0; i--)    /* delete preceding arguments */
  2010.         {
  2011.             histarg (p, & len);
  2012.             p += len;
  2013.         }
  2014.  
  2015.         /* p points to beginning of first wanted arg */
  2016.         /* remove leading blanks */
  2017.         c = *p++;
  2018.         while (c == SP || c == TAB)
  2019.             c = *p++;
  2020.         
  2021.         sub = p - 1;
  2022.  
  2023.  
  2024.         if (str[si] == '-')
  2025.         {
  2026.             si++;
  2027.             if (digit(str[si]))
  2028.                 val = min (ctoi (str, &si), last) - val + 1;
  2029.             else
  2030.             {
  2031.                 val = last - val + 1;
  2032.  
  2033.                 if (str[si] != '\0')
  2034.                     si++;
  2035.             }
  2036.  
  2037.             p = sub;
  2038.             histarg (p, & len);
  2039.             while (val > 0 && len > 0)
  2040.             {
  2041.                 val--;
  2042.                 p += len;
  2043.                 histarg (p, &len);
  2044.             }
  2045.             *p = '\0';
  2046.         }
  2047.         else
  2048.         {
  2049.             histarg (sub, & len);
  2050.             sub [len] = '\0';
  2051.         }
  2052.     }
  2053.  
  2054.     /* move everything to beginning of buffer */
  2055.     if (save != sub)
  2056.     {
  2057.         movstr (sub, save);
  2058.         sub = save;
  2059.     }
  2060.  
  2061.  
  2062.     /*
  2063.      * check that the remaining characters represent
  2064.      * legal following characters
  2065.      */
  2066.  
  2067.     if (str[si] && str[si] != HISTSUB && str[si] != NL)
  2068.         errmsg (str, badtoken);
  2069.     
  2070.     /* check for no substitutions and return if we are done */
  2071.     if (str[si] && str[si] != HISTSUB)
  2072.         return (TRUE);
  2073.     
  2074.     /* keep performing substitutions until there are no more */
  2075.  
  2076.     while (str[si] == HISTSUB)
  2077.     {
  2078.         i = 0;
  2079.         si++;
  2080.         flag = FALSE;
  2081.         /* buf is what to look for */
  2082.         while (str[si] && str[si] != HISTSUB)
  2083.         {
  2084.             if (str[si] == ESCAPE)
  2085.                 si++;
  2086.             
  2087.             if (str[si])
  2088.                 buf[i++] = str[si++];
  2089.         }
  2090.         buf[i] = '\0';
  2091.  
  2092.         i = 0;
  2093.         if (str[si])
  2094.             si++;
  2095.         
  2096.         /* rep is replacement */
  2097.         while (str[si] && str[si] != HISTSUB)
  2098.         {
  2099.             if (str[si] == ESCAPE)
  2100.                 si++;
  2101.             
  2102.             if (str[si])
  2103.                 rep[i++] = str[si++];
  2104.         }
  2105.         rep[i] = '\0';
  2106.  
  2107.         if (str[si] == HISTSUB)
  2108.             si++;
  2109.         
  2110.         if (str[si] == 'g' || str[si] == 'G')
  2111.         {
  2112.             flag = TRUE;
  2113.             si++;
  2114.         }
  2115.  
  2116.         j = 0;        /* j indexes new */
  2117.         p = sub;
  2118.         c = *p++;
  2119.         sp = p;        /* save position for backing up */
  2120.         while (c != '\0')
  2121.         {
  2122.             i = 0;
  2123.             while (c && c != buf[i])
  2124.             {
  2125.                 /* copy what doesn't match */
  2126.                 new[j++] = c;
  2127.                 c = *p++;
  2128.                 sp = p;
  2129.             }
  2130.  
  2131.             while (c && buf[i] && c == buf[i])
  2132.             {
  2133.                 /* partial matching */
  2134.                 c = *p++;
  2135.                 i++;
  2136.             }
  2137.  
  2138.             if (buf[i] == '\0')
  2139.             {
  2140.                 /* successful match */
  2141.                 char *cp = rep;
  2142.  
  2143.                 while (*cp)
  2144.                     new[j++] = *cp++;
  2145.                     /* put in replacement text */
  2146.  
  2147.                 if (flag == FALSE)    /* just 1 replacement */
  2148.                 {
  2149.                     new[j++] = c;
  2150.                     while (*p)
  2151.                         new[j++] = *p++;
  2152.                         /* copy the rest */
  2153.                     break;
  2154.                 }
  2155.             }
  2156.             else if (c != '\0')
  2157.             {
  2158.                 /* back up and try again */
  2159.                 new[j++] = *(sp - 1);
  2160.                 p = sp;
  2161.                 c = *p++;
  2162.                 sp = p;
  2163.             }
  2164.         }
  2165.         new[j] = '\0';
  2166.         movstr (new, sub);
  2167.         /* now look for next substitution */
  2168.     }
  2169.  
  2170.     if (save != sub)
  2171.     {
  2172.         movstr (sub, save);
  2173.         sub = save;
  2174.     }
  2175.     
  2176.     j = length (sub) - 2;
  2177.     if (sub[j] == NL)
  2178.         sub[j] = '\0';
  2179.  
  2180.     return (TRUE);
  2181. }
  2182.  
  2183. /* histget --- get a specified string from the history buffers */
  2184.  
  2185. static int histget (hp, sub)
  2186. int hp;
  2187. char *sub;
  2188. {
  2189.     char buf[BIGBUF];
  2190.     int i, j, maxinx, hval;
  2191.  
  2192.     *sub = '\0';
  2193.     maxinx = (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST + Histline - 1;
  2194.     if (hp < Histline || hp > maxinx)        /* out of range */
  2195.         return (FALSE);
  2196.     
  2197.     hval = ((hp - Histline + Hptrlast - 1) % MAXHIST) + 1;
  2198.     for (i = Histptr[hval]; Histbuf[i] != '\0'; )
  2199.     {
  2200.         int k; 
  2201.  
  2202.         j = 0;
  2203.         while (Histbuf[i] != '\0' && j < sizeof(buf) - 1)
  2204.         {
  2205.             buf[j] = Histbuf[i];
  2206.             i = (i + 1) % HISTSIZE;
  2207.             j++;
  2208.         }
  2209.  
  2210.         buf[j] = '\0';
  2211.         /* strcat (sub, buf); */
  2212.         movstr (buf,
  2213.             sub + (((k = length (sub) - 1) <= 0 ? 0 : k)));
  2214.     }
  2215.  
  2216.     return (TRUE);
  2217. }
  2218.  
  2219. /* histarg --- return the last position of the next argument */
  2220.  
  2221. static void histarg (ptr, len)
  2222. char *ptr;
  2223. int *len;
  2224. {
  2225.     char *p;
  2226.     char c;
  2227.     int bracket, paren, brace, squote, dquote, bquote, skip;
  2228.  
  2229.     p = ptr;
  2230.     *len = 0;
  2231.     skip = FALSE;
  2232.     bracket = paren = brace = squote = dquote = bquote = 0;
  2233.  
  2234.     repeat
  2235.     {
  2236.         *len += 1;
  2237.         c = *p++;
  2238.         while (skip == FALSE && (c == SP || c == TAB))
  2239.         {
  2240.             c = *p++;
  2241.             *len += 1;
  2242.         }
  2243.  
  2244.         skip = TRUE;
  2245.         switch (c) {
  2246.         case ESCAPE:
  2247.             c = *p++;
  2248.             *len += 1;
  2249.             break;
  2250.  
  2251.         case '[':
  2252.             if (squote == 0 && dquote == 0 && bquote == 0)
  2253.                 bracket++;
  2254.             break;
  2255.  
  2256.         case ']':
  2257.             if (squote == 0 && dquote == 0 && bquote == 0)
  2258.                 bracket--;
  2259.             break;
  2260.  
  2261.         case '(':
  2262.             if (squote == 0 && dquote == 0 && bquote == 0)
  2263.                 paren++;
  2264.             break;
  2265.  
  2266.         case ')':
  2267.             if (squote == 0 && dquote == 0 && bquote == 0)
  2268.                 paren--;
  2269.             break;
  2270.  
  2271.         case '{':
  2272.             if (squote == 0 && dquote == 0 && bquote == 0)
  2273.                 brace++;
  2274.             break;
  2275.  
  2276.         case '}':
  2277.             if (squote == 0 && dquote == 0 && bquote == 0)
  2278.                 brace--;
  2279.             break;
  2280.  
  2281.         case '\'':
  2282.             if (dquote == 0 && bquote == 0)
  2283.                 squote = 1 - squote;
  2284.             break;
  2285.  
  2286.         case '"':
  2287.             if (squote == 0 && bquote == 0)
  2288.                 dquote = 1 - dquote;
  2289.             break;
  2290.  
  2291.         case '`':
  2292.             if (dquote == 0 && squote == 0)
  2293.                 bquote = 1 - bquote;
  2294.             break;
  2295.  
  2296.         }
  2297.     } until (c == '\0' ||
  2298.         ((c == SP || c == TAB) && paren == 0 && brace == 0 &&
  2299.         bracket == 0 && squote == 0 && dquote == 0 && bquote == 0));
  2300.  
  2301.     *len -= 1;
  2302.     return;
  2303. }
  2304.  
  2305. /* ctoi --- character to integer conversion, updates indices ala Fortrash */
  2306.  
  2307. static int ctoi (str, inx)
  2308. register char *str;
  2309. register int *inx;
  2310. {
  2311.     register int ret = 0;
  2312.     int neg = 0;
  2313.  
  2314.     if (str[*inx] == '-')
  2315.     {
  2316.         neg = 1;
  2317.         *inx += 1;
  2318.     }
  2319.  
  2320.     while (digit (str[*inx]))
  2321.     {
  2322.         ret = 10 * ret + str[*inx] - '0';
  2323.         *inx += 1;
  2324.     }
  2325.  
  2326.     return (neg ? -ret : ret);
  2327. }
  2328.  
  2329. /* min --- real function to return min of two numbers */
  2330.  
  2331. static int min (a, b)
  2332. register int a, b;
  2333. {
  2334.     return (a < b ? a : b);
  2335. }
  2336.  
  2337. /* histinit --- reinitialize history buffers */
  2338.  
  2339. static void histinit ()
  2340. {
  2341.     Hbuffirst = Hbuflast = Hptrfirst = Hptrlast = Histline = 0;
  2342.     event_count = 1;
  2343. }
  2344.  
  2345. /* histsave --- save history command lines */
  2346.  
  2347. histsave (file)
  2348. char *file;
  2349. {
  2350.     int fd, status, junk;
  2351.  
  2352.     if ((flags&nohistflg) != 0)
  2353.         return (FALSE);
  2354.  
  2355.     if ((flags&prompt) == 0)
  2356.         return (FALSE);
  2357.  
  2358.     if ((fd = creat (file, 0600)) < 0)    /* delete previous contents */
  2359.         return (FALSE);
  2360.     
  2361.     status = TRUE;
  2362.  
  2363.     junk = MAXHIST;
  2364.     if (write (fd, & junk, sizeof (junk)) != sizeof (junk))
  2365.         status = FALSE;
  2366.  
  2367.     junk = HISTSIZE;
  2368.     if (status == TRUE &&
  2369.         write (fd, & junk, sizeof (junk)) != sizeof (junk))
  2370.         status = FALSE;
  2371.  
  2372.     if (status == TRUE &&
  2373.         write (fd, &Hbuffirst, sizeof(Hbuffirst)) != sizeof (Hbuffirst))
  2374.         status = FALSE;
  2375.  
  2376.     if (status == TRUE &&
  2377.         write (fd, & Hbuflast, sizeof (Hbuflast)) != sizeof (Hbuflast))
  2378.         status = FALSE;
  2379.  
  2380.     if (status == TRUE &&
  2381.         write (fd, &Hptrfirst, sizeof(Hptrfirst)) != sizeof (Hptrfirst))
  2382.         status = FALSE;
  2383.  
  2384.     if (status == TRUE &&
  2385.         write (fd, & Hptrlast, sizeof (Hptrlast)) != sizeof (Hptrlast))
  2386.         status = FALSE;
  2387.  
  2388.     if (status == TRUE &&
  2389.         write (fd, Histptr, sizeof(Histptr)) != sizeof (Histptr))
  2390.         status = FALSE;
  2391.  
  2392.     if (status == TRUE &&
  2393.         write (fd, Histbuf, sizeof(Histbuf)) != sizeof (Histbuf))
  2394.         status = FALSE;
  2395.     
  2396.     close (fd);
  2397.  
  2398.     if (status == FALSE)
  2399.         unlink (file);    /* remove entirely */
  2400.     
  2401.     return (status);
  2402. }
  2403.  
  2404. /* histrest --- restore a history save file */
  2405.  
  2406. int histrest (file)
  2407. char *file;
  2408. {
  2409.     int fd, status, junk;
  2410.  
  2411.     if (flags&nohistflg)
  2412.         return (FALSE);
  2413.  
  2414.     if ((flags&prompt) == 0)
  2415.         return (FALSE);
  2416.  
  2417.     if ((fd = open (file, 0)) < 0)    /* open for reading */
  2418.         return (FALSE);
  2419.  
  2420.     status = TRUE;
  2421.     if (read (fd, & junk, sizeof (junk)) != sizeof (junk) ||
  2422.             junk != MAXHIST)
  2423.         status = FALSE;
  2424.  
  2425.     if (status == TRUE && read (fd, & junk, sizeof (junk)) != sizeof (junk)
  2426.             || junk != HISTSIZE)
  2427.         status = FALSE;
  2428.  
  2429.     if (status == TRUE &&
  2430.         read (fd, &Hbuffirst, sizeof (Hbuffirst)) != sizeof (Hbuffirst))
  2431.         status = FALSE;
  2432.  
  2433.     if (status == TRUE &&
  2434.         read (fd, & Hbuflast, sizeof (Hbuflast)) != sizeof (Hbuflast))
  2435.         status = FALSE;
  2436.  
  2437.     if (status == TRUE &&
  2438.         read (fd, &Hptrfirst, sizeof (Hptrfirst)) != sizeof (Hptrfirst))
  2439.         status = FALSE;
  2440.  
  2441.     if (status == TRUE &&
  2442.         read (fd, & Hptrlast, sizeof (Hptrlast)) != sizeof (Hptrlast))
  2443.         status = FALSE;
  2444.  
  2445.     if (status == TRUE &&
  2446.         read (fd, Histptr, sizeof(Histptr)) != sizeof (Histptr))
  2447.         status = FALSE;
  2448.  
  2449.     if (status == TRUE &&
  2450.         read (fd, Histbuf, sizeof(Histbuf)) != sizeof (Histbuf))
  2451.         status = FALSE;
  2452.     
  2453.     Histline = - (Hptrfirst - Hptrlast + MAXHIST) % MAXHIST;
  2454.     event_count = 1;
  2455.     close (fd);
  2456.     return (status);
  2457. }
  2458.  
  2459. /* history --- print history buffer, or save or restore buffer to file */
  2460.  
  2461. int history (argc, argv)
  2462. int argc;
  2463. char **argv;
  2464. {
  2465.     int i;
  2466.     char *hf;
  2467.     int (*hfp)();
  2468.  
  2469.     if ((flags&nohistflg) != 0)
  2470.     {
  2471.         if (flags&prompt)
  2472.             prs ("history processing not enabled\n");
  2473.         return 1;    /* failure */
  2474.     }
  2475.  
  2476.     if ((flags & prompt) == 0)    /* shell file */
  2477.         return 1;
  2478.  
  2479.     if (argc == 1)
  2480.     {
  2481.         int j = Histline + 1;
  2482.         int k, l, m; 
  2483.         int neg;
  2484.         char *cp;
  2485.  
  2486. #define outstr(s)    for (cp = s; *cp; cp++) \
  2487.                 if (*cp == NL && *(cp+1))  \
  2488.                     prs_buff ("\\n"); \
  2489.                 else \
  2490.                     prc_buff (*cp)
  2491.  
  2492.  
  2493.         for (i = Hptrlast; i != Hptrfirst; i = (i + 1) % MAXHIST)
  2494.         {
  2495.             neg = FALSE;
  2496.  
  2497.             k = j++;
  2498.             if (k < 0)
  2499.             {
  2500.                 neg = TRUE;
  2501.                 k = -k;
  2502.             }
  2503.             itos (k);
  2504.             l = length (numbuf) - 1;
  2505.             for (m = 3 - l; m > 0; m--)
  2506.                 prc_buff (SP);
  2507.             prc_buff (neg ? '-' : SP);
  2508.             prs_buff (numbuf);
  2509.             prc_buff (COLON);
  2510.             prc_buff (SP);
  2511.             /*
  2512.              * make sure that what we're printing
  2513.              * doesn't wrap around the history buffer.
  2514.              */
  2515.             k = (i % MAXHIST) + 1;
  2516.             if ((k != Hptrfirst && Histptr[i] < Histptr[k]) ||
  2517.                 (k == Hptrfirst && Histptr[i] < Hbuffirst))
  2518.                 outstr (& Histbuf[Histptr[i]]);
  2519.             else
  2520.             {
  2521.                 /* saved text wraps around */
  2522.                 for (l = Histptr[i]; l <= HISTSIZE - 1 &&
  2523.                         Histbuf[l] != '\0'; l++)
  2524.                     if (Histbuf[l] == NL
  2525.             && Histbuf[l + 1 <= HISTSIZE - 1 ? l + 1 : 0] != '\0')
  2526.                         prs_buff ("\\n");
  2527.                     else
  2528.                         prc_buff (Histbuf[l]);
  2529.                 
  2530.                 if (Histbuf[HISTSIZE - 1] != '\0')
  2531.                     outstr (Histbuf);
  2532.             }
  2533.         }
  2534.         return 0;
  2535.     }
  2536.     else if (eq (argv[1], dashi))
  2537.     {
  2538.         histinit ();
  2539.         return 0;
  2540.     }
  2541.     else if (eq (argv[1], dashr))
  2542.         hfp = histrest;
  2543.     else if (eq (argv[1], dashs))
  2544.         hfp = histsave;
  2545.     else
  2546.     {
  2547.         prs(argv[1]);
  2548.         prc(COLON);
  2549.         prs(h_badopt);
  2550.         newline();
  2551.         return 1;
  2552.     }
  2553.  
  2554.     if (argc >= 3)
  2555.         hf = argv[2];
  2556.     else
  2557.         hf = histfnod.namval;
  2558.     
  2559.     return ((*hfp)(hf) != 0);    /* do a save or restore */
  2560. }
  2561. SHAR_EOF
  2562. echo shar: extracting "'sample.shrc'" '(443 characters)'
  2563. if test -f 'sample.shrc'
  2564. then
  2565.     echo shar: over-writing existing file "'sample.shrc'"
  2566. fi
  2567. cat << \SHAR_EOF > 'sample.shrc'
  2568. # .shrc file --- this file will be read every time the shell cranks up
  2569. # if it is in the $HOME directory
  2570.  
  2571. # this is a sample, currently set up to do some Korn shell emulation
  2572.  
  2573. PPID=$+            # set the Parent Process Id
  2574.  
  2575. # source file name given by ENV environment variable
  2576. # and only if $ENV is not this file.
  2577.  
  2578. if [ "$ENV" != "" -a "$ENV" != "$HOME/.shrc" ]
  2579. then
  2580.     . $ENV
  2581. fi
  2582.  
  2583. # put any useful shell functions here, or source a file with them in it.
  2584. SHAR_EOF
  2585. echo shar: extracting "'aliases.sh'" '(1027 characters)'
  2586. if test -f 'aliases.sh'
  2587. then
  2588.     echo shar: over-writing existing file "'aliases.sh'"
  2589. fi
  2590. cat << \SHAR_EOF > 'aliases.sh'
  2591. # aliases.sh --- sample shell functions which do some of what the csh does
  2592.  
  2593. # pushd, popd, and dirs --- written by Chris Bertin
  2594. # Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
  2595. # as modified by Patrick Elam of GTRI
  2596.  
  2597. pushd () {
  2598.     SAVE=`pwd`
  2599.     DSTACK="$SAVE $DSTACK"
  2600.     if [ "$1" = "" ] 
  2601.     then
  2602.         if [ "$DSTACK" = "$SAVE " ]
  2603.         then
  2604.             echo "pushd: directory stack empty."
  2605.             DSTACK=""
  2606.             return 1
  2607.         fi
  2608.         set $DSTACK
  2609.         cd $2
  2610.         shift 2
  2611.         DSTACK="$SAVE $*"
  2612.     else
  2613.         if (cd $1)
  2614.         then
  2615.             cd $1 >&-
  2616.         else
  2617.             popd > /dev/null
  2618.             return 1
  2619.         fi
  2620.     fi
  2621.     dirs
  2622.     return 0
  2623. }
  2624.  
  2625. popd () {
  2626.     if [ "$DSTACK" = "" ] 
  2627.     then
  2628.         echo "popd: Directory statck empty"
  2629.         return 1
  2630.     fi
  2631.     set $DSTACK
  2632.     cd $1
  2633.     shift
  2634.     DSTACK=$*
  2635.     dirs
  2636.     return 0
  2637. }
  2638.  
  2639. dirs () {
  2640.     echo "`pwd` $DSTACK"
  2641.     return 0
  2642. }
  2643.  
  2644. xchng () {    # exchanged top two entries on the stack
  2645.     if [ "$DSTACK" = "" ]
  2646.     then
  2647.         echo exchange directory stack empty
  2648.         return 1
  2649.     else
  2650.         pushd
  2651.         return 0
  2652.     fi
  2653. }
  2654.  
  2655. source () {    # have the shell read a file in the current shell
  2656.     . $*
  2657. }
  2658.  
  2659. bye () { logout ; }
  2660.  
  2661. logout () { exit 0 ; }
  2662. SHAR_EOF
  2663. #    End of shell archive
  2664. exit 0
  2665.  
  2666.